<template>
    <el-dialog
        :visible="true"
        width="800px"
        :fullscreen="fullScreen"
        :append-to-body="true"
        :close-on-click-modal="false"
        :before-close="handleClose">
        <span slot="title" class="el-dialog__title">
            <span class="titleText">公式编辑</span>
            <span class="subtitle">使用数学运算符编辑公式</span>
            <i class="el-icon-full-screen iconClass" v-if="!fullScreen" @click="()=>{fullScreen = true}"></i>
            <i class="el-icon-aim iconClass" v-else @click="()=>{fullScreen = false}"></i>
        </span>
        <div class="bodyBorder">
            <div class="showFormulaDiv">
                <div class="curParamsDiv">
                    <span style="color: #ff0000d9" v-if="activeData.__config__.required">*</span>
                    {{activeData.__config__.label}} = 
                </div>
                <codemirror
                    ref="codemirrorRef"
                    v-model="editorValue"
                    id="editorId"
                    :options="cmOptions">
                </codemirror>
                <div class="errorDiv" v-if="showError">
                    <span style="color: rgb(220 49 34)">{{errorTip}}</span>
                </div>
            </div>
            <div>
                <el-row class="rowInputDisplay">
                    <el-col :span="8">
                        <span class="field-label">业务对象</span>
                        <div class="borderFirstSelect">
                            <el-input prefix-icon="el-icon-search" class="inputDisplay" placeholder="搜索变量" v-model="filterParamsText"></el-input>
                            <el-tree
                                class="filter-tree"
                                :data="formData"
                                :props="defaultProps"
                                default-expand-all
                                :filter-node-method="filterNode"
                                ref="firstTree">
                                <div class="custom-tree-node" slot-scope="{ node, data }" @click="firstNodeClick(node, data)">
                                    <span class="labelTextCss" :title="data.label">{{ data.label }}</span>
                                    <span class="textCss" v-if="!['0', '1'].includes(data.id)">
                                        <span :class="data.labelCss">{{data.type}}</span>
                                    </span>
                                </div>
                            </el-tree>
                        </div>
                    </el-col>
                    <el-col :span="5">
                        <span class="field-label">函数</span>
                        <div class="borderSecondSelect">
                            <el-input prefix-icon="el-icon-search" class="inputDisplay" placeholder="搜索函数" v-model="filterFormulaText"></el-input>
                            <el-tree
                                class="filter-tree"
                                :data="formulaList"
                                :props="defaultProps"
                                default-expand-all
                                @node-click="secondNodeClick"
                                :filter-node-method="filterNode"
                                ref="secondTree">
                                <span class="custom-tree-node" slot-scope="{ node, data }" @mouseover="mouseover(node, data)">
                                    <div :class="!data.showLabel ? 'disabledLabelCss' : '' ">{{ node.label }}</div>
                                </span>
                            </el-tree>
                        </div>
                    </el-col>
                    <el-col :span="11">
                        <span class="field-label"></span>
                        <div class="borderThirdSelect">
                            <div class="thirdLine">{{curFormulaLabel}}</div>
                            <div class="thirdContent" id="thirdText">
                            </div>
                        </div>
                    </el-col>
                </el-row>
            </div>
            <div class="btnDiv">
                <el-button @click="handleClose" size="small">取消</el-button>
                <el-button @click="confirm" size="small" type="primary">确定</el-button>
            </div>
        </div>
    </el-dialog>
</template>
<script>
import {deepClone} from '../../../../utils/index.js'
import { formulaList } from '@/utils/formula.js'
import { calculate } from './mathOperation.js'
// 全局引入codemirror
import {codemirror} from 'vue-codemirror';
// 引入主题 可以从 codemirror/theme/ 下引入多个
import 'codemirror/theme/idea.css'
import 'codemirror/lib/codemirror.css'
import 'codemirror/addon/hint/show-hint.css';
import 'codemirror/addon/hint/show-hint.js';
import 'codemirror/addon/edit/closebrackets.js';
import 'codemirror/addon/edit/closetag.js';
import 'codemirror/addon/edit/matchbrackets.js';
import 'codemirror/addon/edit/matchtags.js';
import "codemirror/mode/sql/sql";
import "codemirror/addon/hint/sql-hint";
// import "codemirror/addon/hint/javascript-hint";
import "codemirror/addon/hint/anyword-hint";
// 引入语言模式 可以从 codemirror/mode/ 下引入多个
// import 'codemirror/mode/javascript/javascript.js';
import 'codemirror/mode/mathematica/mathematica.js';
export default {
    name: 'FormulaDialog',
    components: {codemirror},
    data () {
        return {
            fullScreen: false,
            filterParamsText: '',
            filterFormulaText: '',
            defaultProps: {
                children: 'children',
                label: 'label'
            },
            formulaList: formulaList,
            formData: [],
            curFormulaLabel: '',
            editorValue: null,
            cmOptions: {
                // 开启自动刷新
	            autorefresh: true,
                // 语言及语法模式
                mode: 'text/x-sql',
                // 主题
                theme: 'idea',
                height: 'auto',
                autofocus: true,
                line: true,
                // 显示行数
                lineNumbers: false,
                // 软换行
                lineWrapping: true,
                // tab宽度
                tabSize: 4,
                matchTags: { bothTags: true },
	            matchBrackets: true, // 在光标点击紧挨{、]括号左、右侧时，自动突出显示匹配的括号 }、]
                autoCloseBrackets: true, // 自动标签闭合
                autoCloseTag: true,
                hintOptions: {
                    // completeSingle: false,
                    // hint: this.handleShowHint()
                },
                // extraKeys: {Ctrl: 'autocomplete'},
            },
            showError: false,
            editor: '',
            CodeMirror: '',
            errorTip: '',
            finaEditorValue: null,
            // 数学函数结构
            curFormParmasLst: [],
        }
    },
    props: ['activeData', 'drawingList'],
    watch: {
        filterParamsText (val) {
            this.$refs['firstTree'].filter(val)
        },
        filterFormulaText (val) {
            this.$refs['secondTree'].filter(val)
        },
        showError (newVal, oldVal) {
            if (newVal) {
                document.getElementsByClassName('CodeMirror')[0].style.height = '130px'
            } else {
                document.getElementsByClassName('CodeMirror')[0].style.height = '160px'
            }
        },
    },
    mounted () {
        this.curFormParmasLst = []
        this.loopGetComp(this.drawingList, false)
        this.formData = [{
            id: '0',
            label: '当前表单字段',
            value: '0',
            children: [...this.curFormParmasLst]
        }]
        if (this.activeData.__config__.formulaStr) {
            const otherStr = this.activeData.__config__.formulaStr.split(/\$.*?\#/g)
            let newStr = otherStr.join('')
            // 先赋值给编辑器，以适配后面有字段要插入，需要原始内容
            this.editorValue = newStr
        }
        this.$nextTick(() => {
            // 赋予全局变量
            this.CodeMirror = require("codemirror/lib/codemirror");
            this.editor = this.$refs.codemirrorRef.codemirror;

            // 先清空当前mode自带的关键字，再添加自定义关键字(关键字高亮是覆盖了原生样式.cm-s-idea span.cm-keyword,样式里有写)
            this.CodeMirror.resolveMode("text/x-sql").keywords = {}
            this.formulaList.forEach(child => {
                child.children.forEach(keyObj => {
                    this.CodeMirror.resolveMode("text/x-sql").keywords[keyObj.label.toLocaleLowerCase()] = true;
                })
            })

            // 命令使用方法
            // this.CodeMirror.execCommand('delWrappedLineRight')

            // 公式回显
            if (this.activeData.__config__.formulaStr) {
                // 分隔出的字段集合
                let signList = this.activeData.__config__.formulaStr.match(/\$.*?\#/g)
                if (signList?.length > 0) {
                    // 分隔开的其余字符串
                    const otherStr = this.activeData.__config__.formulaStr.split(/\$.*?\#/g)
                    signList.forEach((signField, index) => {
                        // 取出字段field
                        const field = signField.slice(1, signField.length - 1).trim()
                        const fieldObj = this.curFormParmasLst.find(comp => {
                            return comp.value === field
                        })
                        let label = ''
                        if (!fieldObj) {
                            label = '已删除字段'
                            signList[index] = `${label}·${field}`
                        } else {
                            label = fieldObj.label
                            signList[index] = `${fieldObj.label}·${field}`
                        }
                        // 适配组件改名情况，重新获取在当前字符串长度的位置
                        const curIndex = this.getPosition(otherStr, signList, index)
                        // 插入小部件
                        this.insertWidget(label, field, { line: 0, ch:curIndex }, fieldObj ? true : false)
                    })
                } else {
                    this.editorValue = this.activeData.__config__.formulaStr
                }
            }

            // 自动提示
            // this.editor.on('cursorActivity', (e) => {
            //     // 传入处理自动提示的函数
            //     this.editor.showHint(this.handleShowHint());
            // });
        })
    },
    methods: {
        // 循环获取数字和计数器组件
        loopGetComp (list, exsitFlag) {
            list.forEach(item => {
                if (item.__config__.tag === 'el-card' || item.__config__.tag === 'el-tabs' || item.__config__.tag === 'Steps'|| item.__config__.tag === 'el-collapse') {
                    item.__config__.children.forEach(comp => {
                        this.loopGetComp(comp.__config__.children)
                    })
                } else if (item.__config__.tag === 'Subform') {
                    exsitFlag = item.__config__.children[0].__config__.children.find(comp => {
                        return this.activeData.__vModel__ === comp.__vModel__
                    })
                    if (exsitFlag) {
                        this.curFormParmasLst = item.__config__.children[0].__config__.children.filter(filterComp => {
                            return ['number', 'numbers', 'input', 'textarea'].includes(filterComp.__config__.tagIcon)
                        }).map(newComp => {
                            return {
                                label: newComp.__config__.label,
                                value: newComp.__vModel__,
                                labelCss: 'textTypeCss',
                                type: ['input', 'textarea'].includes(newComp.__config__.tagIcon) ? '文本' : '数字'
                            }
                        })
                    }
                } else {
                    if (!exsitFlag && (['number', 'numbers', 'input', 'textarea'].includes(item.__config__.tagIcon))) {
                        this.curFormParmasLst.push({
                            label: item.__config__.label,
                            value: item.__vModel__,
                            labelCss: 'textTypeCss',
                            type: ['input', 'textarea'].includes(item.__config__.tagIcon) ? '文本' : '数字'
                        })
                    }
                }
            })
        },
        handleShowHint () {
            const cur = this.editor.getCursor();	// 获取当前最新输入
            const curLine = this.editor.getLine(cur.line);	// 获取当前所在行数
            const end = cur.ch;	// 获取最新输入结束位置
            const start = end - 1;	// 设置检测输入的起始位置（本案例中只检测最新输入的一个字符）
            const word = curLine.charAt(start);	// 获取最新输入的内容（本案例中为最新输入的一个字符）
            const paramsData = this.formulaList[0].children.map(func => {
                return func.label
            })
            // list为筛选出的自动提示项的数组
            const list = paramsData.filter(param => {
                return param.indexOf(word.toUpperCase()) > -1;
            });
            const _this = this
            return {
                hint: function() {
                    return {
                        from: _this.CodeMirror.Pos(cur.line, start),	// 需要自动替换的起始位置（本案例中为最新输入的一个字符的位置）
                        to: _this.CodeMirror.Pos(cur.line, end),	// 需要自动替换的终止位置（本案例中为当前输入最末端）
                        list,	// 由自动提示项组成的数组
                    }
                }
            };
        },
        // 回显时确定当前field的位标
        getPosition (otherStr, signList, idx) {
            let newStr = ''
            for (let x = 0; x < otherStr.length; x++) {
                const other = otherStr[x]
                newStr = newStr + other
                if (x === idx) {
                    return newStr.length
                }
                newStr = newStr + signList[x]
            }
        },
        // 函数名称点击
        secondNodeClick (node, data) {
            if (!node.showLabel || data.label.indexOf('函数') !== -1) {
                return
            }
            this.insertContent('formula', node.label + '()')
        },
        // 测试代码，勿删
        inputChange(editor) {
            editor.showHint();
            editor.setOption('hintOptions', {
                // 自定义提示选项
                completeSingle: false, // 当匹配只有一项的时候是否自动补全
                tables: this.handleShowHint(),
            });
        },
        filterNode(value, data) {
            if (!value) return true;
            return data.label.toUpperCase().indexOf(value.toUpperCase()) !== -1;
        },
        firstNodeClick (node, data) {
            this.insertContent('text', data.label, data.value)
        },
        insertContent (flag, content, field) {
            if (content.indexOf('当前表单') !== -1) {
                return
            }
            let pos1 = this.editor.getCursor()
            let pos2 = {};
            pos2.line = pos1.line;
            pos2.ch = pos1.ch;
            if (flag === 'formula') {
                this.editor.replaceRange(`${content}`, pos2);
                pos2.ch = pos1.ch + content.length - 1
                // 光标位于括号内侧
                this.editor.focus()
                setTimeout(()=>{
                    this.editor.setCursor(pos2)
                }, 0)
            } else {
                this.insertWidget(content, field, pos2, true)
                // 添加小部件(仅限上方或者下方, 不适用此类场景)
                // this.editor.addWidget(pos2, fieldSpan, false);
            }
        },
        insertWidget (content, field, pos2, exsitFlag) {
            //制作标签dom(颜色样式自行设置)
            var dom = document.createElement("span");
            if (exsitFlag) {
                // 正常字段mark样式
                dom.className = "cm-field cm-field-value";
            } else {
                // 被删除字段mark样式
                dom.style.background = 'rgb(255 211 211 / 59%)';
                dom.style.color = "#f83636";
                dom.style.display = 'inline-block';
                dom.style.fontSize = '13px';
                dom.style.padding = '2px';
                dom.style.margin = '0px 2px';
            }
            dom.setAttribute('data-field', field)
            dom.innerHTML = `${content}·${field}`;
            // 使用空串作 占位符
            const fillingSign = ' '.repeat(content.length - 1)
            //先插入字符串
            let endPos;
            if (this.editorValue) {
                const str1 = this.editorValue.substring(0, pos2.ch).replaceAll(' ', '')
                const str2 = this.editorValue.substring(0, pos2.ch + 1).replaceAll(' ', '')
                const firstStr = str1.charAt(str1.length - 1)
                const secondStr = str2.charAt(str2.length - 1)
                if ((firstStr === '#' && (secondStr === ')' || secondStr === '$' || secondStr === ',')) ||
                (firstStr === ',' && secondStr === '$')) {
                    this.editor.replaceRange(`,$${fillingSign}${field}#`, pos2);
                    // ch加1是因为底下加了 , 分隔符
                    pos2.ch++
                    endPos = { line: pos2.line, ch: pos2.ch + content.length + field.length + 1};
                } else {
                    this.editor.replaceRange(`$${fillingSign}${field}#`, pos2);
                    // 长度加1是因为dom中间有英文分隔符
                    endPos = { line: pos2.line, ch: pos2.ch + content.length + field.length + 1};
                }
            } else {
                this.editor.replaceRange(`$${fillingSign}${field}#`, pos2);
                // 长度加1是因为dom中间有英文分隔符
                endPos = { line: pos2.line, ch: pos2.ch + content.length + field.length + 1};
            }
            //再对插入字符串进行标签制定（其实就是在codemirror中动态添加标签）
            this.editor.markText(pos2, endPos, {
                replacedWith: dom,
                atomic: true,
            });
            // 光标位于部件后面
            this.editor.focus()
            setTimeout(()=>{
                this.editor.setCursor(endPos)
            }, 0)
        },
        // 生成指定长度占位符
        makeBlankMark (length) {
            let str = ''
            for (let a = 0; a < length; a++) {
                str += ' '
            }
            return str
        },
        // 拼接字符串
        joinStr (strList, valueList) {
            // 将strList 与 valueList 数组合并
            let newStr = ''
            strList.forEach((other, index) => {
                newStr = newStr + other
                if (valueList[index] !== undefined) {
                    newStr = newStr + valueList[index]
                }
            })
            return newStr
        },
        mouseover (node, data) {
            if (data.label.indexOf('函数') === -1) {
                this.curFormulaLabel = data.label
                const curTextHtml = data.content.replaceAll(data.label, "<span style='color:#761086'>"+ data.label +"</span>")
                document.getElementById('thirdText').innerHTML = curTextHtml
            }
        },
        confirm () {
            console.log('editorValue', this.editorValue)
            // const xx = this.$refs.codemirrorRef.codemirror.getAllMarks();
            // xx.forEach(mark => {
            // 获取每个markText的位置
            //     const yy = mark.find()
            //     console.log('yy', yy)
            // })
            if (this.editorValue) {
                if (this.editorValue.indexOf('#$') !== -1) {
                    this.showError = true
                    this.errorTip = '字段之间缺少逗号或者运算符';
                    return false;
                }
                const regEn = /[`~!@^&?_:{}\;]/im;
                const regCn = /[·！￥（——）：；“”‘、，|《。》？、【】]/im;
                // 字段之间的符号不进行校验
                const otherItemStr = this.editorValue.split(/\$.*?\#/g).join('')
                if(regEn.test(otherItemStr) || regCn.test(otherItemStr)) {
                    this.showError = true
                    this.errorTip = '不能包含特殊字符';
                    return false;
                }
                // 校验是否完美括号
                const flag = this.valiateBracket(this.editorValue);
                // calculate(this.editorValue)
                if (!flag) {
                    this.showError = true
                    this.errorTip = '括号未完全闭合，请检查';
                } else {
                    this.showError = false
                    this.$emit('closeFormulaDialog', this.editorValue)
                }
            } else {
                this.$emit('closeFormulaDialog', this.editorValue)
            }
        },
        handleClose () {
            this.$emit('closeFormulaDialog', false)
        },
        // 校验是否完美括号
        valiateBracket (curStr) {
            let bracketStr = ''
            let middleBracketStr = ''
            // 去掉小括号之外的其余字符
            bracketStr = curStr.replace(/[^\(^\)]/g,'')
            // 去掉中括号之外的其余字符
            middleBracketStr = curStr.replace(/[^\[^\]]/g,'')
            const one = this.removeBothBracket(bracketStr, '()')
            const two = this.removeBothBracket(middleBracketStr, '[]')
            if (!one || !two) {
                return false
            }
            return true
        },
        // 去除 括号
        removeBothBracket (str, mark) {
            let newStr = str
            let length = newStr.length % 2 === 0 ? newStr.length / 2 : (newStr.length + 1) / 2
            for (let a = 0; a < length; a++) {
                newStr = newStr.replaceAll(mark, '')
            }
            if (newStr.length > 0) {
                return false
            }
            return true
        }
    }
}
</script>
<style lang="less" scoped>
/deep/.el-dialog__title{
    width: 100%;
}
/deep/.cm-s-idea span.cm-operator {
    color: lightseagreen;
}
/deep/.cm-s-idea span.cm-keyword{
    color: #781287;
    font-family: emoji;
    font-size: 12px;
}
/deep/.cm-s-idea span.cm-type{
    color: #781287;
    line-height: 1em;
    font-weight: bold;
}
/deep/.cm-field.cm-field-value {
    background: #EBF5FF;
    color: #008DCD;
    display: inline-block;
    font-size: 12px;
    padding: 3px;
    margin: 0px 2px;
}
.disabledLabelCss{
    color: lightgrey;
}
.textCss{
    width: auto;
    text-align: center;
    height: 18px;
    line-height: 18px;
    margin-right: 10px;
    float: right;
}
.textTypeCss {
    color: #F4B700;
    background: rgba(244,183,0,.15);
    border-radius: 10px;
    padding: 0 8px;
}
.labelTextCss{
    display: inline-block;
    width: 120px;
    white-space:nowrap;
    overflow:hidden;
    text-overflow:ellipsis;
}
/deep/.CodeMirror {
    height: 160px;
}
.filter-tree{
    overflow-y: auto;
    height: 210px;
}
.titleText{
    font-size: 15px;
}
/deep/.textTip{
    padding: 2px 4px;
    margin: 0 4px;
    background: #EBF5FF !important;
    -webkit-border-radius: 3px;
    -moz-border-radius: 3px;
    border-radius: 3px;
    color: #008DCD !important;
}
.errorTip{
    background: #FFF1F1;
    color: #7D241C;
    height: 25px;
    line-height: 25px;
    padding-left: 10px;
    font-size: 12px;
}
/deep/.rowInputDisplay .el-input__inner{
    border: none;
}
.rowInputDisplay {
    font-size: 12px;
}
.thirdContent{
    padding: 10px;
    white-space: pre-line;
}
.thirdLine{
    height: 37px;
    line-height: 37px;
    padding-left: 10px;
    border-bottom: 1px solid lightgrey;
}
.inputDisplay{
    border: none;
    border-bottom: 1px solid lightgrey;
}
/deep/.el-textarea__inner{
    resize: none;
    border: none;
}
.btnDiv{
    float: right;
    margin-top: 20px;
}
.borderFirstSelect{
    border: 1px solid lightgrey;
    height: 250px;
    margin-right: 15px;
    font-size: 12px;
}
.borderSecondSelect{
    border: 1px solid lightgrey;
    height: 250px;
    font-size: 12px;
}
.borderThirdSelect{
    border: 1px solid lightgrey;
    height: 250px;
    margin: 7px 0px 0px 15px;
    font-size: 12px;
}
.custom-tree-node{
    font-size: 12px;
}
.field-label {
    font-size: 12px;
    color: #91A1B7;
    line-height: 18px;
    margin-bottom: 5px;
    display: inline-block;
}
.errorDiv{
    height: 32px;
    line-height: 32px;
    background: #F3F8FB;
    font-size: 12px;
    padding-left:10px;
}
.curParamsDiv{
    height: 36px;
    line-height: 36px;
    background: #F3F8FB;
    font-size: 12px;
    padding-left:10px;
}
.showFormulaDiv{
    height: 200px;
    margin-bottom: 15px;
    border: 1px solid lightgrey;
}
.bodyBorder{
    padding: 20px 0;
}
/deep/.el-dialog__header {
    padding: 10px 10px 10px 20px!important;
    border-bottom: 1px solid lightgrey;
}
/deep/.el-dialog__body {
    padding: 0px 20px !important;
    height: 580px;
}
/deep/.el-dialog__headerbtn {
    top: 15px;
}
.iconClass{
    float:right;
    margin-right: 35px;
    line-height: 1.8;
}
.subtitle{
    margin-left:20px;
    display: inline-block;
    font-size: 12px;
    color: #91A1B7;
}
.dialogBody{
    height: 600px;
}
/deep/::-webkit-scrollbar {
  /*滚动条整体样式*/
  width: 5px; /*高宽分别对应横竖滚动条的尺寸*/
  height: 5px;
}
/deep/::-webkit-scrollbar-thumb {
  background-color: skyblue;
  background-image: -webkit-linear-gradient(
    45deg,
    rgba(255, 255, 255, 0.2) 25%,
    transparent 25%,
    transparent 50%,
    rgba(255, 255, 255, 0.2) 50%,
    rgba(255, 255, 255, 0.2) 75%,
    transparent 75%,
    transparent
  );
  /*滚动条里面小方块*/
//   border-radius: 10px;
}
/deep/::-webkit-scrollbar-track {
  background: #ededed;
//   border-radius: 10px;
  /*滚动条里面轨道*/
  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
}
</style>